using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using TelnetExpect;
using System.IO;
using System.Diagnostics;

namespace Winform1
{
    public partial class Form1 : Form
    {
        TelnetStream m_telnet;
        TextBoxBaseTerminalAnsiTelnetHandlerBase m_termTelnetHandler;
        RtfTbAnsiConsole m_console;
        StreamWriter m_wtr;
        System.Threading.ManualResetEvent m_quit;
        volatile bool m_connected;
        //
        ConnectDialog m_ConnectDialog;


        public Form1()
        {
            InitializeComponent();
        }

        //----
        private bool IsConnected
        {
            get { return m_telnet != null && m_connected; }
        }

        void Connect()
        {
            Connect("localhost");
        }

        void Connect(string hostName)
        {
            try {
                m_telnet = TelnetStream.Connect(hostName);
            } catch (System.Net.Sockets.SocketException soex) {
                m_telnet = null;
                MessageBox.Show("Connect failed: '" + soex.Message + "' (" + soex.NativeErrorCode + ")",
                    "Connect failed");
                return;
            }
            bool doAnsi = this.checkBoxAnsi.Checked;
            if (doAnsi) {
                m_termTelnetHandler = new TextBoxBaseTerminalAnsiTelnetHandlerBase(this.richTextBox1);
                m_telnet.AddOptionHandler(m_termTelnetHandler);
            }
            WinFormsAuthenticationNtlmTelnetHandler authH = new WinFormsAuthenticationNtlmTelnetHandler(this);
            authH.SetTargetHostName(hostName);
            m_telnet.AddOptionHandler(authH);
            //
            m_console = new RtfTbAnsiConsole(m_telnet, this.richTextBox1);
            System.Threading.ThreadPool.QueueUserWorkItem(Printer);
            //
            m_wtr = new StreamWriter(m_telnet);
            this.richTextBox1.Clear();
            this.richTextBox1.Focus();
            m_connected = true;
        }

        private void Disconnect()
        {
            m_connected = false;
            m_console.OnText(Environment.NewLine + Environment.NewLine
                + "[Disconnecting...]");
            m_wtr.Close();
        }

        //--
        private void buttonConnect1_Click(object sender, EventArgs e)
        {
            //WAS-- Connect();
            //
            if (m_ConnectDialog == null) {
                m_ConnectDialog = new ConnectDialog();
            }
            DialogResult rslt = m_ConnectDialog.ShowDialog();
            if (rslt == DialogResult.OK) {
                Connect(m_ConnectDialog.HostName);
            }
        }

        private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Disconnect();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        //----
        void Printer(object dummy)
        {
            try {
                m_quit = new System.Threading.ManualResetEvent(false);
                m_console.RunPrinterForever();
            } catch (IOException ioex) {
                if (m_connected) { // Show the error if close unexpected.
                    m_console.OnText(Environment.NewLine + Environment.NewLine
                        + "[Connection failed with:" + Environment.NewLine
                        + ioex.ToString() + "]");
                }
            } finally {
                m_connected = false;
                m_quit.Set();
                m_console.OnText(Environment.NewLine + Environment.NewLine
                    + "[Connection to host lost.]");
            }
        }


        class TextBoxBaseTerminalAnsiTelnetHandlerBase : TerminalAnsiTelnetHandlerBase
        {
            TextBoxBase m_tb;

            public TextBoxBaseTerminalAnsiTelnetHandlerBase(TextBoxBase tb)
            {
                m_tb = tb;
            }

            protected override void GetWindowSize(out int width, out int height)
            {
                width = 80;
                height = 24;
            }
        }

        //--------
        private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
        {
            if (!IsConnected)
                return;
            Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                "_KeyDown: {0}", e.KeyData));
            bool ansi = IsAnsiTerminalMode(m_telnet);
            bool handled = m_console.WinFormsWriteKeyPressTo(e, ansi, m_wtr);
            e.Handled = handled;
            e.SuppressKeyPress = handled;
            m_wtr.Flush(); // just in case handled is wrong....
            Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                "    _KeyDown: {0} handle", handled ? "DID" : "did NOT"));
        }

        private void richTextBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!IsConnected)
                return;
            Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                "_KeyPress: '{0}' = 0x{1:X4}", e.KeyChar, (int)e.KeyChar));
            if (IsAnsiTerminalMode() && e.KeyChar == '\r') {
                Trace.WriteLine("_KeyPress: got enter! (" + TelnetBaseStream.UtilityShowControlChars(e.KeyChar) + ")");
                return;
            }
            if (e.KeyChar == '\r' || e.KeyChar == '\n') {
            }
            if (e.KeyChar < 0x20) {
                // Leave for KeyUp
                return;
            }
            m_wtr.Write(e.KeyChar);
            m_wtr.Flush();
            e.Handled = true;
            Trace.WriteLine("    _KeyPress: sent");
        }

        private void richTextBox1_KeyUp(object sender, KeyEventArgs e)
        {
            if (!IsConnected)
                return;
            Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                "_KeyUp: {0}", e.KeyData));
            SendAsciiControlKeys(sender, e);
        }

        bool SendAsciiControlKeys(object sender, KeyEventArgs e)
        {
            char? ch = null;
            if (e.Modifiers == Keys.Control) {
                if ((e.KeyCode >= Keys.A && e.KeyCode <= Keys.Z)) {
                    int num = e.KeyCode - Keys.A + 1;
                    ch = (char)num;
                }
                // TODO (Need to handle Ctrl+@ -> 0)
                //else if (e.KeyCode == (Keys)0) { //@@@@@@@@
                //    ch = (char)0;
                //}
            } else if (e.Modifiers == Keys.None) {
                // We don't send any <0x20 in KeyPress so we have to handle keys
                // that generate such codes here.
                if (e.KeyData == Keys.Tab) {
                    ch = '\t';
                } else if (e.KeyData == Keys.Back) {
                    ch = '\b';
                } else if (e.KeyData == Keys.Escape) {
                    ch = '\x1B';
                }
            }
            if (ch.HasValue) {
                Trace.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
                    "    SendAsciiControlKeys sending: 0x{0:X2}", (int)ch.Value));
                m_wtr.Write(ch.Value);
                m_wtr.Flush();
                return true;
            }
            return false;
        }

        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!IsConnected)
                return;
            string txt = Clipboard.GetText();
            m_wtr.Write(txt);
            m_wtr.Flush();
        }

        bool IsAnsiTerminalMode()
        {
            if (!IsConnected)
                return false; // whatever...
            return IsAnsiTerminalMode(m_telnet);
        }

        static bool IsAnsiTerminalMode(TelnetStream telnet)
        {
            bool enabled = telnet.GetLocalState(TelnetOption.TerminalType);
            // We should ideally check we send "ANSI" mode (last) -- but we only
            // send the single term-type "ANSI" so the server BETTER support it!
            return enabled;
        }

        //----
        private void tcommandBrkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MenuSendTelnetCommand(TelnetBaseStream.Command.BRK);
        }

        private void tcommandIpToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MenuSendTelnetCommand(TelnetBaseStream.Command.IP);
        }

        private void tcommandAoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MenuSendTelnetCommand(TelnetBaseStream.Command.AO);
        }

        private void tcommandAytToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MenuSendTelnetCommand(TelnetBaseStream.Command.AYT);
        }

        private void tcommandEcToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MenuSendTelnetCommand(TelnetBaseStream.Command.EC);
        }

        private void tcommandElToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MenuSendTelnetCommand(TelnetBaseStream.Command.EL);
        }

        void MenuSendTelnetCommand(TelnetBaseStream.Command cmd)
        {
            if (m_telnet == null || !m_connected)
                return;
            m_telnet.SendTelnetCommand(cmd);
        }

        private void tabToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_wtr.Write('\t');
            m_wtr.Flush();
        }

    }
}
